package trading_strategy.orders;

import org.apache.log4j.Logger;
import trading_strategy.instruments.ListedInstrument;

/**
 * Class that handles an order state.
 * It ensures that, at any time, no more than 1 order is alive on the market
 * If a new request arrives while an order is alive, it will first cancel the first order and then send the following one

 * Created by IntelliJ IDEA.
 * User: gderoujoux
 * Date: 6 mai 2010
 * Time: 15:19:09
 * To change this template use File | Settings | File Templates.
 */
public class SingleOrderAutomaton implements IOrderAutomaton {
    private static final Logger LOGGER = Logger.getLogger(SingleOrderAutomaton.class);

    public void cancel() {
        synchronized (this) {
            if (lastSentOrder instanceof IOCLimitOrder) {
                LOGGER.warn("Trying to send an order while an IOC has just been sent - ignoring");
            } else if (lastSentOrder != null && state.isAliveInMarket() && !cancelRequested) {
                broker.sendCancelOrder(lastSentOrder);
            }
        }
    }

    public static enum State {
        NOT_SENT(false), SENT(true), REFUSED(false), ON_MARKET(true), EXECUTED(false), CANCEL_SENT(true), CANCELLED(false);
        boolean aliveInMarket;

        State(boolean aliveInMarket) {
            this.aliveInMarket = aliveInMarket;
        }

        public boolean isAliveInMarket() {
            return aliveInMarket;
        }
    }

    State state = State.NOT_SENT;
    Order lastSentOrder;
    Order nextOrderToSend;
    boolean cancelRequested = false;
    final OrderProvider orderProvider;
    final ListedInstrument instrument;
    final InstrumentOrderBroker broker;


    public SingleOrderAutomaton(ListedInstrument instrument, OrderProvider orderProvider) {
        this.orderProvider = orderProvider;
        this.instrument = instrument;
        this.broker = Brokers.getInstance().getBroker(instrument);
        if (broker == null) {
            throw new IllegalArgumentException("Could not find declared broker for instrument " + instrument);
        }
    }

    public void sendOrder(Order order) {
        synchronized (this) {
            if (this.nextOrderToSend != null) {
                // Replace the unsent order with new value
            }
            if (lastSentOrder != null && state.isAliveInMarket()) {
                // We may need to cancel the existing order first
            }
            this.nextOrderToSend = order;
        }
        send();
    }

    public void send() {
        synchronized (this) {
            if (nextOrderToSend == null) {
                // Nothing to send
                return;
            }

            if (state.isAliveInMarket()) {
                cancel();
            } else {
                if (nextOrderToSend.isCancelRequested() || !orderProvider.shouldStillSendOrder(nextOrderToSend)) {
                    // Ignore as a cancel as been requested before beeing processed by this automaton
                    nextOrderToSend = null;
                } else {
                    LOGGER.info("Sending order " + nextOrderToSend);
                    lastSentOrder = nextOrderToSend;
                    this.state = State.SENT;
                    broker.sendNewOrder(nextOrderToSend);
                    // TODO: send the order
                    nextOrderToSend = null;
                }
            }
        }
    }

    @Override
    public void onOrderState(State s) throws OrderException {
        synchronized (this) {
            switch (s) {
                case REFUSED:
                    if (state != State.SENT) {
                        throw new OrderException("Received a bad state : " + s + " while being " + state);
                    }
                    state = s;
                    // Notify order refused
                    break;
                case ON_MARKET:
                    if (state != State.SENT) {
                        throw new OrderException("Received a bad state : " + s + " while being " + state);
                    }
                    state = s;
                    break;
                case EXECUTED:
                    state = s;
                    // Notify executed qty
                    break;
                case CANCELLED:
                    state = s;
                    lastSentOrder = null;
                    boolean wasCancelRequested = cancelRequested;
                    cancelRequested = false;
                    if (nextOrderToSend != null && wasCancelRequested) {
                        send(); // We received the cancel, now send the desired new order
                    }
                    break;
            }
        }
    }

    @Override
    public void onOrderExecuted(int qty, double price, Order order) throws OrderException {
        synchronized (this) {
            switch (state) {
                case NOT_SENT:
                    throw new OrderException("Received execution while order in incorrect state : " + state);
                default:
                    orderProvider.onExecution(qty, price, order);
                    break;
            }
        }
    }

    public boolean hasOrderActive() {
        synchronized (this) {
            return state.isAliveInMarket();
        }
    }

    public interface OrderProvider {
        /**
         * Called just before sending an order, if this order was delayed for some reson
         *
         * @param order the order to be checked
         * @return true if the order, that was requested sometime in the past, should still be sent
         */
        public boolean shouldStillSendOrder(Order order);

        public void onExecution(int qty, double price, Order order);
    }
}
